Completed
Pull Request — master (#328)
by
unknown
02:21
created

FileList.findFile   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 18
rs 9.4285
1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global _, Gallery, Thumbnails */
13
/**
14
 * OCA.FileList methods needed for file uploading
15
 *
16
 * This hack makes it possible to use the Files scripts as is, without having to import and
17
 * maintain them in Gallery
18
 *
19
 * Empty methods are for the "new" button, if we want to implement that one day
20
 *
21
 * @type {{findFile: FileList.findFile, createFile: FileList.createFile,
22
 *     getCurrentDirectory: FileList.getCurrentDirectory, getUploadUrl:
23
 *     FileList.getUploadUrl}}
24
 */
25
var FileList = {
0 ignored issues
show
Bug introduced by
Redefinition of 'FileList'.
Loading history...
26
	/**
27
	 * Makes sure the filename does not exist
28
	 *
29
	 * Gives an early chance to the user to abort the action, before uploading everything to the
30
	 * server.
31
	 * Albums are not supported as we don't have a full list of images contained in a sub-album
32
	 *
33
	 * @param fileName
34
	 * @returns {*}
35
	 */
36
	findFile: function (fileName) {
37
		"use strict";
38
		var path = Gallery.currentAlbum + '/' + fileName;
39
		var galleryImage = Gallery.imageMap[path];
40
		if (galleryImage) {
41
			var fileInfo = {
42
				name: fileName,
43
				directory: Gallery.currentAlbum,
44
				path: path,
45
				etag: galleryImage.etag,
46
				mtime: galleryImage.mTime * 1000, // Javascript gives the Epoch time in milliseconds
47
				size: galleryImage.size
48
			};
49
			return fileInfo;
50
		} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
51
			return null;
52
		}
53
	},
54
55
	/**
56
	 * Create an empty file inside the current album.
57
	 *
58
	 * @param {string} name name of the file
59
	 *
60
	 * @return {Promise} promise that will be resolved after the
61
	 * file was created
62
	 *
63
	 */
64
	createFile: function(name) {
65
		var self = this;
66
		var deferred = $.Deferred();
67
		var promise = deferred.promise();
68
69
		OCA.Files.isFileNameValid(name);
70
71
		var targetPath = this.getCurrentDirectory() + '/' + name;
72
73
		//Check if file already exists
74
		if(Gallery.imageMap[targetPath]) {
75
			OC.Notification.showTemporary(
76
				t('files', 'Could not create file "{file}" because it already exists', {file: name})
77
			);
78
			deferred.reject();
79
			return promise;
80
		}
81
82
		Gallery.filesClient.putFileContents(
83
			targetPath,
84
			'',
85
			{
86
				contentType: 'text/plain',
87
				overwrite: true
88
			}
89
			)
90
			.done(function() {
91
				// TODO: error handling / conflicts
92
				Gallery.filesClient.getFileInfo(
93
					targetPath, {
94
						properties: self.findFile(targetPath)
95
					}
96
					)
97
					.then(function(status, data) {
98
						deferred.resolve(status, data);
99
					})
100
					.fail(function(status) {
101
						OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
102
						deferred.reject(status);
103
					});
104
			})
105
			.fail(function(status) {
106
				if (status === 412) {
107
					OC.Notification.showTemporary(
108
						t('files', 'Could not create file "{file}" because it already exists', {file: name})
109
					);
110
				} else {
111
					OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
112
				}
113
				deferred.reject(status);
114
			});
115
116
		return promise;
117
	},
118
119
120
	/**
121
	 * Retrieves the current album
122
	 *
123
	 * @returns {string}
124
	 */
125
	getCurrentDirectory: function () {
126
		"use strict";
127
128
		// In Files, dirs start with a /
129
		return '/' + Gallery.currentAlbum;
130
	},
131
132
	/**
133
	 * Retrieves the WebDAV upload URL
134
	 *
135
	 * @param {string} fileName
136
	 * @param {string} dir
137
	 *
138
	 * @returns {string}
139
	 */
140
	getUploadUrl: function (fileName, dir) {
141
		if (_.isUndefined(dir)) {
142
			dir = this.getCurrentDirectory();
143
		}
144
145
		var pathSections = dir.split('/');
146
		if (!_.isUndefined(fileName)) {
147
			pathSections.push(fileName);
148
		}
149
		var encodedPath = '';
150
		_.each(pathSections, function (section) {
151
			if (section !== '') {
152
				encodedPath += '/' + encodeURIComponent(section);
153
			}
154
		});
155
		return OC.linkToRemoteBase('webdav') + encodedPath;
156
	}
157
};
158
159
/**
160
 * OCA.Files methods needed for file uploading
161
 *
162
 * This hack makes it possible to use the Files scripts as is, without having to import and
163
 * maintain them in Gallery
164
 *
165
 * @type {{isFileNameValid: Files.isFileNameValid, generatePreviewUrl: Files.generatePreviewUrl}}
166
 */
167
var Files = {
168
	App: {fileList: {}},
169
170
	isFileNameValid: function (name) {
171
		"use strict";
172
		var trimmedName = name.trim();
173
		if (trimmedName === '.' || trimmedName === '..') {
174
			throw t('files', '"{name}" is an invalid file name.', {name: name});
175
		} else if (trimmedName.length === 0) {
176
			throw t('files', 'File name cannot be empty.');
177
		} else if (OC.fileIsBlacklisted(trimmedName)) {
178
			throw t('files', '"{name}" is not an allowed filetype', {name: name});
179
		}
180
181
		return true;
182
183
	},
184
185
	/**
186
	 * Generates a preview for the conflict dialogue
187
	 *
188
	 * Since Gallery uses the fileId and Files uses the path, we have to use the preview endpoint
189
	 * of Files
190
	 */
191
	generatePreviewUrl: function (urlSpec) {
192
		"use strict";
193
		var previewUrl;
194
		var path = urlSpec.file;
195
196
		// In Files, root files start with //
197
		if (path.indexOf('//') === 0) {
198
			path = path.substring(2);
199
		} else {
200
			// Directories start with /
201
			path = path.substring(1);
202
		}
203
204
		if (Gallery.imageMap[path]) {
205
			var fileId = Gallery.imageMap[path].fileId;
206
			var thumbnail = Thumbnails.map[fileId];
207
			previewUrl = thumbnail.image.src;
208
		} else {
209
			var previewDimension = 96;
210
			urlSpec.x = Math.ceil(previewDimension * window.devicePixelRatio);
211
			urlSpec.y = Math.ceil(previewDimension * window.devicePixelRatio);
212
			urlSpec.forceIcon = 0;
213
			previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
214
		}
215
216
		return previewUrl;
217
	}
218
};
219
220
/**
221
 * The FileSummary class encapsulates the file summary values and
222
 * the logic to render it in the given container
223
 *
224
 * @constructs FileSummary
225
 * @memberof OCA.Files
226
 *
227
 * @param $tr table row element
0 ignored issues
show
Documentation introduced by
The parameter $tr does not exist. Did you maybe forget to remove this comment?
Loading history...
228
 * @param {OC.Backbone.Model} [options.filesConfig] files app configuration
0 ignored issues
show
Documentation introduced by
The parameter options.filesConfig does not exist. Did you maybe forget to remove this comment?
Loading history...
229
 */
230
var FileSummary = function() {
231
	this.clear();
232
};
233
234
FileSummary.prototype = {
235
	summary: {
236
		totalFiles: 0,
237
		totalDirs: 0,
238
		totalHidden: 0,
239
		totalSize: 0,
240
		sumIsPending:false
241
	},
242
243
	/**
244
	 * Returns whether the given file info must be hidden
245
	 *
246
	 * @param {OC.Files.FileInfo} fileInfo file info
0 ignored issues
show
Documentation introduced by
The parameter fileInfo does not exist. Did you maybe forget to remove this comment?
Loading history...
247
	 *
248
	 * @return {boolean} true if the file is a hidden file, false otherwise
249
	 */
250
	_isHiddenFile: function(file) {
251
		return file.name && file.name.charAt(0) === '.';
252
	},
253
254
	/**
255
	 * Adds file
256
	 * @param {OC.Files.FileInfo} file file to add
257
	 */
258
	add: function(file) {
259
		if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
260
			this.summary.totalDirs++;
261
		}
262
		else {
263
			this.summary.totalFiles++;
264
		}
265
		if (this._isHiddenFile(file)) {
266
			this.summary.totalHidden++;
267
		}
268
269
		var size = parseInt(file.size, 10) || 0;
270
		if (size >=0) {
271
			this.summary.totalSize += size;
272
		} else {
273
			this.summary.sumIsPending = true;
274
		}
275
	},
276
	/**
277
	 * Removes file
278
	 * @param {OC.Files.FileInfo} file file to remove
279
	 */
280
	remove: function(file) {
281
		if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
282
			this.summary.totalDirs--;
283
		}
284
		else {
285
			this.summary.totalFiles--;
286
		}
287
		if (this._isHiddenFile(file)) {
288
			this.summary.totalHidden--;
289
		}
290
		var size = parseInt(file.size, 10) || 0;
291
		if (size >=0) {
292
			this.summary.totalSize -= size;
293
		}
294
	},
295
	/**
296
	 * Returns the total of files and directories
297
	 */
298
	getTotal: function() {
299
		return this.summary.totalDirs + this.summary.totalFiles;
300
	},
301
	/**
302
	 * Recalculates the summary based on the given files array
303
	 * @param files array of files
304
	 */
305
	calculate: function(files) {
306
		var file;
307
		var summary = {
308
			totalDirs: 0,
309
			totalFiles: 0,
310
			totalHidden: 0,
311
			totalSize: 0,
312
			sumIsPending: false
313
		};
314
315
		for (var i = 0; i < files.length; i++) {
316
			file = files[i];
317
			if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
318
				summary.totalDirs++;
319
			}
320
			else {
321
				summary.totalFiles++;
322
			}
323
			if (this._isHiddenFile(file)) {
324
				summary.totalHidden++;
325
			}
326
			var size = parseInt(file.size, 10) || 0;
327
			if (size >=0) {
328
				summary.totalSize += size;
329
			} else {
330
				summary.sumIsPending = true;
331
			}
332
		}
333
		this.setSummary(summary);
334
	},
335
	/**
336
	 * Clears the summary
337
	 */
338
	clear: function() {
339
		this.calculate([]);
340
	},
341
	/**
342
	 * Sets the current summary values
343
	 * @param summary map
344
	 */
345
	setSummary: function(summary) {
346
		this.summary = summary;
347
	}
348
};
349
350
var FilesFiles = {
351
	/**
352
	 * Returns the download URL of the given file(s)
353
	 * @param {string} filename string or array of file names to download
354
	 * @param {string} [dir] optional directory in which the file name is, defaults to the current directory
355
	 * @param {bool} [isDir=false] whether the given filename is a directory and might need a special URL
0 ignored issues
show
Documentation Bug introduced by
The parameter isDir=false does not exist. Did you maybe mean isDir instead?
Loading history...
356
	 */
357
	getDownloadUrl: function(filename, dir, isDir) {
358
		if (!_.isArray(filename) && !isDir) {
359
			var pathSections = dir.split('/');
360
			pathSections.push(filename);
361
			var encodedPath = '';
362
			_.each(pathSections, function(section) {
363
				if (section !== '') {
364
					encodedPath += '/' + encodeURIComponent(section);
365
				}
366
			});
367
			return OC.linkToRemoteBase('webdav') + encodedPath;
368
		}
369
370
		if (_.isArray(filename)) {
371
			filename = JSON.stringify(filename);
372
		}
373
374
		var params = {
375
			dir: dir,
376
			files: filename
377
		};
378
		return this.getAjaxUrl('download', params);
379
	},
380
381
	/**
382
	 * Returns the ajax URL for a given action
383
	 * @param action action string
384
	 * @param params optional params map
385
	 */
386
	getAjaxUrl: function(action, params) {
387
		var q = '';
388
		if (params) {
389
			q = '?' + OC.buildQueryString(params);
390
		}
391
		return OC.filePath('files', 'ajax', action + '.php') + q;
392
	},
393
394
	/**
395
	 * Handles the download and calls the callback function once the download has started
396
	 * - browser sends download request and adds parameter with a token
397
	 * - server notices this token and adds a set cookie to the download response
398
	 * - browser now adds this cookie for the domain
399
	 * - JS periodically checks for this cookie and then knows when the download has started to call the callback
400
	 *
401
	 * @param {string} url download URL
402
	 * @param {function} callback function to call once the download has started
403
	 */
404
	handleDownload: function(url, callback) {
405
		var randomToken = Math.random().toString(36).substring(2),
406
			checkForDownloadCookie = function() {
407
				if (!OC.Util.isCookieSetToValue('ocDownloadStarted', randomToken)){
408
					return false;
409
				} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
410
					callback();
411
					return true;
412
				}
413
			};
414
415
		if (url.indexOf('?') >= 0) {
416
			url += '&';
417
		} else {
418
			url += '?';
419
		}
420
		OC.redirect(url + 'downloadStartSecret=' + randomToken);
421
		OC.Util.waitFor(checkForDownloadCookie, 500);
422
	}
423
};
424
425
OCA.Files = Files;
426
OCA.Files.App.fileList = FileList;
427
OCA.Files.FileSummary = FileSummary;
428
OCA.Files.Files = FilesFiles;
429
430